/*
 * port_posix.c -  POSIX 平台硬件抽象层实现
 *
 * Author: xiaobo@YGL (xiaobocwb@gmail.com)
 * Version: 1.0.0
 * Date: 2025-01-14
 */


#include "port_posix.h"

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <time.h>
#include <termios.h>
#include <semaphore.h>
#include <pthread.h>

/*******************************************************************************
 * 硬件抽象层
 *******************************************************************************/
int open_serial_port(const char *device) {
    int fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0) {
        printf("[C] Error opening %s: %s\n", device, strerror(errno));
        return -1;
    }

    struct termios tty;
    memset(&tty, 0, sizeof(tty));
    if (tcgetattr(fd, &tty) != 0) {
        printf("[C] Error from tcgetattr: %s\n", strerror(errno));
        close(fd);
        return -1;
    }

    cfsetospeed(&tty, B115200);
    cfsetispeed(&tty, B115200);

    tty.c_cflag |= (CLOCAL | CREAD);    
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;                 
    tty.c_cflag &= ~PARENB;             
    tty.c_cflag &= ~CSTOPB;             
    tty.c_cflag &= ~CRTSCTS;            

    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    tty.c_cc[VMIN] = 0; 
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("[C] Error from tcsetattr: %s\n", strerror(errno));
        close(fd);
        return -1;
    }

    return fd;
}

int uart_recv(void *dev, uint8_t *buf, uint16_t size, uint32_t timeout_ms) {
    if (!dev || !buf || size == 0) {
        return -1;
    }
    int fd = *(int*)dev;
    struct pollfd fds;
    fds.fd = fd;
    fds.events = POLLIN;
    
    int ret = poll(&fds, 1, timeout_ms);
    if (ret < 0) {
        printf("Poll error: %s\n", strerror(errno));
        return -1;
    }
    
    if (ret == 0) {
        return 0;
    }
    
    ssize_t bytes_read = read(fd, buf, size);
    if (bytes_read < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return 0;
        }
        printf("Read error: %s", strerror(errno));
        return -1;
    }
    
    return (int)bytes_read;
}

static int uart_send(void *dev, uint8_t *data, uint16_t len) {
    if (!dev || !data || len == 0) {
        return -1;
    }
    int fd = *(int*)dev;
    return write(fd, data, len);
}

static void* hal_sem_new(void) {
    sem_t* sem = malloc(sizeof(sem_t));
    sem_init(sem, 0, 0);
    return sem;
}

static void hal_sem_del(void *sem) {
    free(sem);
}

static bool hal_sem_wait(void* sem, uint32_t wait_tick) {
    sem_t* semaphore = (sem_t*)sem;

    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += wait_tick / 1000;
    ts.tv_nsec += (wait_tick % 1000) * 1000000;

    return sem_timedwait(semaphore, &ts) == 0;
}

static void hal_sem_post(void* sem) {
    sem_t* semaphore = (sem_t*)sem;
    sem_post(semaphore);
}

static void* hal_mutex_new(void)
{
    pthread_mutex_t* mutex = malloc(sizeof(pthread_mutex_t));
    pthread_mutex_init(mutex, NULL);
    return mutex;
}

static void hal_mutex_del(void* mutex)
{
    pthread_mutex_destroy(mutex);
}

static void hal_mutex_lock(void* mutex)
{
    pthread_mutex_lock(mutex);
}

static void hal_mutex_unlock(void* mutex)
{
    pthread_mutex_unlock(mutex);
}

const protocol_hal_t hal_posix = {
    .dev = NULL,
    .send = uart_send,

    .sem_new = hal_sem_new,
    .sem_del = hal_sem_del,
    .sem_wait = hal_sem_wait,
    .sem_post = hal_sem_post,

    .mutex_new = hal_mutex_new,
    .mutex_del = hal_mutex_del,
    .mutex_lock = hal_mutex_lock,
    .mutex_unlock = hal_mutex_unlock
};
